/******************************************************************************
 * Copyright 2022 The AIR Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *****************************************************************************/

#include "v2x-asn-msgs-internal.h"

namespace {
ssize_t _s_string_2_voidp(const std::string &str, void **result,
                          malloc_func_t malloc_func = nullptr) {
  if (result == nullptr) {
    return V2X_ASN_ERR_INVALID_PARAM;
  }
  if (malloc_func == nullptr) {
    malloc_func = malloc;
  }
  *result = malloc_func(str.length() + 1);
  if (*result == nullptr) {
    return V2X_ASN_ERR_NO_MEMORY;
  }
  memcpy(*result, str.data(), str.length());
  return (ssize_t)str.length();
}
}  // namespace

V2X_ASN_FUNC(message_frame_uper2xer) {
  if (!buffer || !buffer_len || !result) {
    return V2X_ASN_ERR_INVALID_PARAM;
  }
  auto asn_msg = ASN1(MessageFrame)::decode_ptr(ATS_UNALIGNED_BASIC_PER, buffer,
                                                buffer_len);
  if (!asn_msg) {
    return V2X_ASN_ERR_UPER_DECODE;
  }
  auto len =
      ASN1(MessageFrame)::encode_tp(ATS_BASIC_XER, asn_msg.get(), result);
  if (len < 0) {
    return V2X_ASN_ERR_XER_ENCODE;
  }
  return len;
}

V2X_ASN_FUNC(message_frame_xer2uper) {
  if (!buffer || !buffer_len || !result) {
    return V2X_ASN_ERR_INVALID_PARAM;
  }
  auto asn_msg =
      ASN1(MessageFrame)::decode_ptr(ATS_BASIC_XER, buffer, buffer_len);
  if (!asn_msg) {
    return V2X_ASN_ERR_XER_DECODE;
  }
  auto len = ASN1(MessageFrame)::encode_tp(ATS_UNALIGNED_BASIC_PER,
                                           asn_msg.get(), result);
  if (len < 0) {
    return V2X_ASN_ERR_UPER_ENCODE;
  }
  return len;
}

V2X_ASN_FUNC(message_frame_uper2pbstr) {
  if (!buffer || !buffer_len || !result) {
    return V2X_ASN_ERR_INVALID_PARAM;
  }
  // ASN 解码
  auto message_asn = ASN1(MessageFrame)::decode_ptr(ATS_UNALIGNED_CANONICAL_PER,
                                                    buffer, buffer_len);
  if (!message_asn) {
    LOG(ERROR) << "ERR_UPER_DECODE";
    return V2X_ASN_ERR_UPER_DECODE;
  }
  auto message_pb = std::make_shared<v2xpb::asn::MessageFrame>();
  if (!message_pb) {
    LOG(ERROR) << "ERR_NO_MEMORY";
    return V2X_ASN_ERR_NO_MEMORY;
  }
  switch (message_asn->present) {
    case MessageFrame_PR_bsmFrame: {
      VLOG(3) << "BSM Received";
      if (!bsm_asn2pb(&message_asn->choice.bsmFrame,
                      message_pb->mutable_bsmframe())) {
        LOG(ERROR) << "Failed to convert BSM";
        return V2X_ASN_ERR_CONV;
      }
      break;
    }
    case MessageFrame_PR_mapFrame: {
      VLOG(3) << "MAP Received";
      if (!map_asn2pb(&message_asn->choice.mapFrame,
                      message_pb->mutable_mapframe())) {
        LOG(ERROR) << "Failed to convert MAP";
        return V2X_ASN_ERR_CONV;
      }
      break;
    }
    case MessageFrame_PR_rsmFrame: {
      VLOG(3) << "RSM Received";
      if (!rsm_asn2pb(&message_asn->choice.rsmFrame,
                      message_pb->mutable_rsmframe())) {
        LOG(ERROR) << "Failed to convert RSM";
        return V2X_ASN_ERR_CONV;
      }
      break;
    }
    case MessageFrame_PR_spatFrame: {
      VLOG(3) << "SPAT Received";
      if (!spat_asn2pb(&message_asn->choice.spatFrame,
                       message_pb->mutable_spatframe())) {
        LOG(ERROR) << "Failed to convert Spat";
        return V2X_ASN_ERR_CONV;
      }
      break;
    }
    case MessageFrame_PR_rsiFrame: {
      VLOG(3) << "RSI Received";
      if (!rsi_asn2pb(&message_asn->choice.rsiFrame,
                      message_pb->mutable_rsiframe())) {
        LOG(ERROR) << "Failed to convert RSI";
        return V2X_ASN_ERR_CONV;
      }
      break;
    }
    default:
      LOG(WARNING) << "Unsupported: " << message_asn->present;
      return V2X_ASN_ERR_INTERNAL;
  }
  auto len = int64_t(message_pb->ByteSizeLong());
  auto ptr = malloc_func(len);
  if (!ptr) {
    LOG(ERROR) << "ERR_NO_MEMORY";
    return V2X_ASN_ERR_NO_MEMORY;
  }
  if (!message_pb->SerializePartialToArray(ptr, static_cast<int>(len))) {
    free(ptr);
    return V2X_ASN_ERR_INTERNAL;
  }
  *result = ptr;
  return len;
}

V2X_ASN_FUNC(message_frame_pbstr2uper) {
  if (!buffer || !buffer_len) {
    LOG(ERROR) << "NULLPTR";
    return V2X_ASN_ERR_INVALID_PARAM;
  }
  if (buffer_len > INT_MAX) {
    LOG(ERROR) << "TOO LONG";
    return V2X_ASN_ERR_INVALID_PARAM;
  }
  auto message_pb = std::make_shared<v2xpb::asn::MessageFrame>();
  if (!message_pb) {
    LOG(ERROR) << "ERR_NO_MEMORY";
    return V2X_ASN_ERR_NO_MEMORY;
  }
  if (!message_pb->ParsePartialFromArray(buffer,
                                         static_cast<int>(buffer_len))) {
    LOG(ERROR) << "Failed to parse pb string";
    return V2X_ASN_ERR_CONV;
  }
  // ASN 解码
  auto message_asn = ASN1(MessageFrame)::create_ptr();
  if (!message_asn) {
    LOG(ERROR) << "ERR_NO_MEMORY";
    return V2X_ASN_ERR_NO_MEMORY;
  }
  switch (message_pb->payload_case()) {
    case v2xpb::asn::MessageFrame::PayloadCase::kBsmFrame: {
      VLOG(3) << "BSM Received";
      message_asn->present = MessageFrame_PR_bsmFrame;
      if (!bsm_pb2asn(message_pb->bsmframe(), &message_asn->choice.bsmFrame)) {
        //                LOG(ERROR) << "Failed to convert BSM";
        return V2X_ASN_ERR_CONV;
      }
      break;
    }
    case v2xpb::asn::MessageFrame::PayloadCase::kMapFrame: {
      VLOG(3) << "MAP Received";
      message_asn->present = MessageFrame_PR_mapFrame;
      if (!map_pb2asn(message_pb->mapframe(), &message_asn->choice.mapFrame)) {
        //                LOG(ERROR) << "Failed to convert MAP";
        return V2X_ASN_ERR_CONV;
      }
      break;
    }
    case v2xpb::asn::MessageFrame::PayloadCase::kRsmFrame: {
      VLOG(3) << "RSM Received";
      message_asn->present = MessageFrame_PR_rsmFrame;
      if (!rsm_pb2asn(message_pb->rsmframe(), &message_asn->choice.rsmFrame)) {
        LOG(ERROR) << "Failed to convert BSM";
        return V2X_ASN_ERR_CONV;
      }
      break;
    }
    case v2xpb::asn::MessageFrame::PayloadCase::kSpatFrame: {
      VLOG(3) << "SPAT Received";
      message_asn->present = MessageFrame_PR_spatFrame;
      if (!spat_pb2asn(message_pb->spatframe(),
                       &message_asn->choice.spatFrame)) {
        //                LOG(ERROR) << "Failed to convert SPAT";
        return V2X_ASN_ERR_CONV;
      }
      break;
    }
    case v2xpb::asn::MessageFrame::PayloadCase::kRsiFrame: {
      VLOG(3) << "RSI Received";
      message_asn->present = MessageFrame_PR_rsiFrame;
      if (!rsi_pb2asn(message_pb->rsiframe(), &message_asn->choice.rsiFrame)) {
        LOG(ERROR) << "Failed to convert RSI";
        return V2X_ASN_ERR_CONV;
      }
      break;
    }
    default:
      break;
  }
  auto len = ASN1(MessageFrame)::encode_tp(ATS_UNALIGNED_BASIC_PER,
                                           message_asn.get(), result);
  if (len < 0) {
    return V2X_ASN_ERR_UPER_ENCODE;
  }
  return len;
}

V2X_ASN_FUNC(message_frame_map_xml2uper) {
  if (!buffer || !buffer_len) {
    LOG(ERROR) << "NULLPTR";
    return V2X_ASN_ERR_INVALID_PARAM;
  }
  std::string content((const char *)buffer, buffer_len);
  pugi::xml_document map_xml;
  map_xml.load_string(content.c_str());
  // ==========================================================================================
  auto msg_asn = ASN1(MessageFrame)::create_ptr();
  msg_asn->present = MessageFrame_PR_mapFrame;
  auto *map_asn = &msg_asn->choice.mapFrame;
  if (!map_xml2asn(map_xml, map_asn)) {
    return V2X_ASN_ERR_CONV;
  }
  // ==========================================================================================
  auto result_asn =
      ASN1(MessageFrame)::encode_str(ATS_UNALIGNED_BASIC_PER, msg_asn.get());
  if (result_asn.empty()) {
    return V2X_ASN_ERR_CONV;
  }
  return _s_string_2_voidp(result_asn, result, malloc_func);
}
